module queue (clock, reset, i_d, i_v, i_r, o_d, o_v, o_r);

	parameter depth = 16; // - greatest #items in queue (2 <= depth <= 256)
	parameter width = 16; // - width of data (i_d, o_d)
	input clock;
	input reset;
	input [width-1:0] i_d; // - input stream data (concat data + eos)
	input i_v; // - input stream valid
	wire i_b; // - input stream back-pressure
	output [width-1:0] o_d; // - output stream data
	output o_v; // - output stream valid
	wire o_b; // - output stream back-pressure
	
	output i_r;
	input o_r;
	
	assign i_r = ~i_b;
	assign o_b = ~o_r;

	parameter addrwidth =
	( (((depth)) ==0) ? 0 // - depth==0 LOG2=0
			: (((depth-1)>>0)==0) ? 0 // - depth<=1 LOG2=0
			: (((depth-1)>>1)==0) ? 1 // - depth<=2 LOG2=1
			: (((depth-1)>>2)==0) ? 2 // - depth<=4 LOG2=2
			: (((depth-1)>>3)==0) ? 3 // - depth<=8 LOG2=3
			: (((depth-1)>>4)==0) ? 4 // - depth<=16 LOG2=4
			: (((depth-1)>>5)==0) ? 5 // - depth<=32 LOG2=5
			: (((depth-1)>>6)==0) ? 6 // - depth<=64 LOG2=6
			: (((depth-1)>>7)==0) ? 7 // - depth<=128 LOG2=7
			: 8) // - depth<=256 LOG2=8
			;
	reg [addrwidth-1:0] addr, addr_, a_; // - SRL16 address
	// for data output
	reg shift_en_; // - SRL16 shift enable
	reg [width-1:0] srl [depth-1:0]; // - SRL16 memory
	parameter state_empty = 1'b0; // - state empty : o_v=0 o_d=UNDEFINED
	parameter state_nonempty = 1'b1; // - state nonempty: o_v=1 o_d=srl[addr]
	// #items in srl = addr+1
	reg state, state_; // - state register
	
	wire addr_full_; // - true iff addr==depth-1
	wire addr_zero_; // - true iff addr==0
	assign addr_full_ = (addr==depth-1); // - queue full
	assign addr_zero_ = (addr==0); // - queue contains 1
	assign o_d = srl[addr]; // - output data from queue
	assign o_v = (state==state_empty) ? 0 : 1; // - output valid if non-empty
	assign i_b = addr_full_; // - input bp if full
	
	always @(posedge clock or negedge reset) begin // - seq always: FFs
		if (!reset) begin
			state <= state_empty;
			addr <= 0;
		end
	else begin
			state <= state_;
			addr <= addr_;
		end
	end // always @ (posedge clock or negedge reset)
	always @(posedge clock) begin // - seq always: SRL16
	// - infer enabled SRL16 from shifting srl array
	// - no reset capability; srl[] contents undefined on reset
	if (shift_en_) begin
	// synthesis loop_limit 256
	for (a_=depth-1; a_>0; a_=a_-1) begin
		srl[a_] <= srl[a_-1];
	end
		srl[0] <= i_d;
	end
	end // always @ (posedge clock or negedge reset)
	
		
	always @* begin // - combi always
		case (state)
		state_empty: begin // - (empty, will not produce)
			if (i_v) begin // - empty & i_v => consume
				shift_en_ <= 1;
				addr_ <= 0;
				state_ <= state_nonempty;
			end
			else begin // - empty & !i_v => idle
				shift_en_ <= 0;
				addr_ <= 0;
				state_ <= state_empty;
			end
		end
	
		state_nonempty: begin
			if (addr_full_) begin // - (full, will not consume)
				if (o_b) begin // - full & o_b => idle
					shift_en_ <= 0;
					addr_ <= addr;
					state_ <= state_nonempty;
				end
				else begin // - full & !o_b => produce
					shift_en_ <= 0;
					addr_ <= addr-1;
					state_ <= state_nonempty;
				end
			end
			
			else begin // - (mid: neither empty nor full)
				if (i_v && o_b) begin // - mid & i_v & o_b => consume
					shift_en_ <= 1;
					addr_ <= addr+1;
					state_ <= state_nonempty;
				end
				else if (i_v && !o_b) begin // - mid & i_v & !o_b => cons+prod
					shift_en_ <= 1;
					addr_ <= addr;
					state_ <= state_nonempty;
				end
				else if (!i_v && o_b) begin // - mid & !i_v & o_b => idle
					shift_en_ <= 0;
					addr_ <= addr;
					state_ <= state_nonempty;
				end
				else if (!i_v && !o_b) begin // - mid & !i_v & !o_b => produce
					shift_en_ <= 0;
					addr_ <= addr_zero_ ? 0 : addr-1;
					state_ <= addr_zero_ ? state_empty : state_nonempty;
				end
			end // else: !if(addr_full_)
			end // case: state_nonempty
			
		endcase // case(state)
end // always @ *
endmodule // queue